
import fs from 'node:fs';
import { getDB } from './db/db-dm';
import { input, select, confirm, editor } from '@inquirer/prompts';
import  fileSelector from 'inquirer-file-selector'
import _default from 'inquirer-action-select';
const ActionSelect: any = _default;

import StreamWriter from "./util/stream-writer-deepseek";
import { createSQLParser, parseSQLString } from "./util/sql-parser-deepseek";

const originalConsoleError = console.error;
console.error = function(message?: any, ...optionalParams: any[]) {
    optionalParams.push("\n");
    originalConsoleError(message, ...optionalParams);
}

process.on('uncaughtException', (error) => {
  if (error instanceof Error && error.name === 'ExitPromptError') {
    console.log('👋 until next time!');
  } else {
    // Rethrow unknown errors
    throw error;
  }
});


type DBType = 'dmdb' | 'mysql' | 'postgresql' | 'sqlite';
interface DataSource {
    name: string;
    type: DBType;
    url: string;
    username: string;
    password: string;
}
interface DataConfig {
    basePath?: string;
    dataSource: DataSource[];
}

const dataConfigFile = 'config.json';
function loadConfig(): DataConfig {
  try {
    if (!fs.existsSync(dataConfigFile)) {
        const dataConfig: DataConfig = {
            dataSource: []
        };
        fs.writeFileSync(dataConfigFile, JSON.stringify(dataConfig, null, 2));
    }
    const data = fs.readFileSync(dataConfigFile, 'utf-8');
    return JSON.parse(data);
  } catch (error) {
    console.error('Error loading config:', error);
    throw error;
  }
}
function saveConfig(config: DataConfig): void {
  try {
    fs.writeFileSync(dataConfigFile, JSON.stringify(config, null, 2));
  } catch (error) {
    console.error('Error saving config:', error);
    throw error;
  }
}
function setBasePath(basePath: string): void {
    const config = loadConfig();
    config.basePath = basePath;
    saveConfig(config);
}
function getBasePath(): string {
    const config = loadConfig();
    return config.basePath || process.cwd();
}
function addDataSource(dataSource: DataSource): void {
    const config = loadConfig();
    config.dataSource.push(dataSource);
    saveConfig(config);
}
function getDataSource(): DataSource[] {
    const config = loadConfig();
    return config.dataSource;
}


enum Action {
    ADD = "添加数据源",
    IMPORT = "导入数据",
    EXPORT = "导出数据",
    QUIT = "退出"
}

function sqlFileSelector(basePath?: string) {
    return fileSelector({
        basePath,
        message: 'Select a sql file',
        allowCancel: true,
        showExcluded: true,
        filter(f) {
            if (f.name.toLowerCase().endsWith('.sql')) {
                return true; // 允许所有文件
            }
            return false;
        },
    });
}

async function addSelection() {
    const name = await input({ message: 'Enter the data source name', required: true });
    const type = await select({
        message: '请选择数据库类型',
        choices: [
            // {
            //     name: 'mysql',
            //     value: 'mysql',
            //     description: 'MySQL is a popular relational database',
            // },
            {
                name: 'dmdb',
                value: 'dmdb',
                description: 'dmdb is a lightweight database',
            },
        ],
    });
    const url = await input({ message: 'Enter the data source URL', required: true });
    const username = await input({ message: 'Enter the data source username', required: true });
    const password = await input({ message: 'Enter the data source password' });
    const dataSource: DataSource = {
        name,
        type: type as DBType, // Default to dmdb, can be changed later
        url,
        username,
        password
    };
    addDataSource(dataSource);
}


// 命令行

async function selectDataSource() {
    // 选择 数据源
    const dataSourceList = getDataSource();
    const selectedSource = await select({
        message: '请选择一个数据源导入',
        choices: dataSourceList.map(ds => ({
            name: ds.name,
            value: ds
        }))
    });
    const db = await getDB(selectedSource.url, selectedSource.username, selectedSource.password);
    try {
        await db.ping();
        console.info('连接到数据库', selectedSource.name);
        return db;
    } catch (error) {
        console.error('连接到数据库时出错:', error);
        return null;
    }
}

async function importSelection() {
    
    // 选择 数据源
    const db = await selectDataSource();
    if (!db) {
        console.error('未能连接到数据库');
        return;
    }

    // 选择模式
    const schemaNames = await db.getSchemaNames();
    const selectedSchema = await select({
        message: '请选择一个模式导入数据',
        choices: schemaNames.map(name => ({ name, value: name }))
    });
    await db.setSchema(selectedSchema);

    // 选择 sql 文件
    const executeSQLFile = (filePath: string): Promise<void> => {
        try {
            const sqlParser = createSQLParser({
                escapeBackslash: true,
                includeComments: false
            });
            
            console.info("读取文件：", filePath);
            const sqlStream = fs.createReadStream(filePath).pipe(sqlParser);
            return new Promise((resolve) => {
                let index = 0;
                sqlStream.on('data', async (stmt: string) => {
                    try {
                        console.info(`执行SQL（${++index}）：${stmt}`);
                        await db.execute(stmt);
                    } catch (error) {
                        console.error('Error executing SQL:', error);
                        await db.rollback();
                    }
                });
                sqlStream.on('end', async () => {
                    await db.commit();
                    console.info('执行完成');
                    resolve()
                });
            });
        } catch (error) {
            console.error('读取 SQL 文件失败:', error);
            throw error;
        }
    };

    const fileSelectionWay = await select({
        message: '请选择导入方式',
        choices: [
            { name: '从文件导入（文件路径）', value: 'filePath' },
            { name: '从文件导入（文件选择器）', value: 'file' },
            { name: '从剪贴板导入', value: 'clipboard' }
        ]
    });
    // 文件路径
    if (fileSelectionWay === 'filePath') {
        const filePath = await input({ message: '请输入 SQL 文件路径', required: true });
        await executeSQLFile(filePath);
    }
    // 文件浏览器
    else if (fileSelectionWay === 'file') {
        const basePath = getBasePath();
        let fileSelection: string | undefined = undefined;
        try {
            fileSelection = await sqlFileSelector(basePath);
            console.info('选择了文件:', fileSelection);
        } catch (error) {
            // do nothing
        }

        if (!fileSelection) {
            console.error('没有选择文件（若无法动作，请按回车）.');
            return;
        }
        await executeSQLFile(fileSelection);
    }
    // 剪切板
    else if (fileSelectionWay === 'clipboard') {
        const editorContent = await editor({
            message: '请在编辑器中输入 SQL 语句',
            default: '',
        });
        if (!editorContent) {
            console.error('编辑器内容为空');
            return;
        }
        const sqlStatements = parseSQLString(editorContent);
        try {
            for (const stmt of sqlStatements) {
                await db.execute(stmt);
            }
            await db.commit();
            console.info('SQL executed successfully\n');
        } catch (error) {
            console.error('Error executing SQL:', error);
        }
    }
}

async function exportSelection() {
    // 选择 数据源
    const db = await selectDataSource();
    if (!db) {
        console.error('未能连接到数据库');
        return;
    }

    // 设置 文件名
    const filePath = await input({
        message: '请输入导出文件名（默认：export.sql）',
        default: 'export.sql',
        required: true
    });
    if (fs.existsSync(filePath)) {
        const ifContinue = await confirm({
            message: "已存在文件，是否继续",
        });
        if (!ifContinue) {
            return;
        }
    }
    fs.writeFileSync(filePath, "");

    // 选择模式
    const schemaNames = await db.getSchemaNames();
    const exportSchema = await select({
        message: '请选择一个模式导出数据',
        choices: schemaNames.map(name => ({ name, value: name }))
    });
    await db.setSchema(exportSchema);
    fs.writeFileSync(
        filePath,
        '-- Exported from schema: ' + exportSchema + '\n\n',
        {
        encoding: "utf-8",
        flag: "a"
        }
    );

    const writer = new StreamWriter(filePath);
    const tableNames = await db.getTableNames(exportSchema);
    for (const tableName of tableNames) {
        console.info("\n导出表", tableName);
        const ddl = await db.getTableDDL(exportSchema, tableName);
        const precessedDDL = ddl.replaceAll(`"${exportSchema}".`, "");
        const ddlExportStr = "-- Export Table " + tableName + "\n" + precessedDDL + "\n";
        writer.writeLine(ddlExportStr);

        console.info("导出表", tableName, "的数据");
        const result = await db.execute<any>(`select * from ${tableName};`)
        const count = result.rows?.length || 0;
        let index = 0;
        result.rows?.forEach((row: any[]) => {
            const rowStr = row
                .map(value => {
                    if (!value) {
                        return null;
                    }
                    if (value instanceof Date) {
                        return `'${value.toString()}'`;
                    }
                    return `'${value.toString()}'`;
                })
                .join(", ");
            const dataExportStr = `INSERT INTO '${tableName}' VALUES (${rowStr});`;
            writer.writeLine(dataExportStr);
            console.info(`表 ${tableName}（${++index}/${count}）`);
        });
        writer.writeLine("\n");
    }
    writer.close();
    console.info(`数据导出成功，文件路径: ${filePath}`);
}

async function main() {
    let quit = false;
    while (!quit) {
        const action = await ActionSelect.default({
            message: '请选择操作',
            actions: [
                { value: Action.ADD, name: Action.ADD, key: 'a' },
                { value: Action.IMPORT, name: Action.IMPORT, key: 'i' },
                { value: Action.EXPORT, name: Action.EXPORT, key: 'e' },
                { value: Action.QUIT, name: Action.QUIT, key: 'q' }
            ],
            choices: [
                { value: Action.ADD, name: Action.ADD },
                { value: Action.IMPORT, name: Action.IMPORT },
                { value: Action.EXPORT, name: Action.EXPORT },
                { value: Action.QUIT, name: Action.QUIT }
            ]
        });
        if (action.answer === Action.QUIT) {
            quit = true;
            process.exit(0);
        } else if (action.answer === Action.ADD) { 
            await addSelection();
        } else if (action.answer === Action.IMPORT) { 
            await importSelection();
        } else if (action.answer === Action.EXPORT) {
            await exportSelection();
        }
    }
}
main();
